package gomysql

import (
	"fmt"
	"go-micro-framework/go_service/core/gomysql/internal"
	"go-micro-framework/go_service/core/goutils"
	"reflect"
	"strings"
)

type QueryWrapper[T any] struct {
	whereSqlQuery []any
	whereArgs     []any
	whereFormat   string
	orderBuilder  strings.Builder
	groupBuilder  strings.Builder
	havingBuilder strings.Builder
	havingArgs    []any
	last          any
	selects       SelectWrapper[T]
	updates       UpdateWrapper[T]
}

type SelectWrapper[T any] struct {
	selectColumns   []string
	omitColumns     []string
	distinctColumns []string
}
type UpdateWrapper[T any] struct {
	updateMap     map[string]any
	columnTypeMap map[string]reflect.Type
}

func NewQuery[T any]() (*QueryWrapper[T], *T) {
	q := &QueryWrapper[T]{}
	t := GetModel[T]()
	return q, t
}

// NewQueryModel 构建查询条件
func NewQueryModel[T any, R any]() (*QueryWrapper[T], *T, *R) {
	q := &QueryWrapper[T]{}
	t := GetModel[T]()
	r := GetModel[R]()
	return q, t, r
}

/** SQL 规范接口 **/
type SqlSegment interface {
	getSqlSegment() string
}

/** where条件字段,格式为eg: u.user_name ;**/
type columnField struct {
	prefix    string //前缀名称,u
	column    string //数据库sql字段名名称,user_name
	fieldName any    //业务对象struct对应的属性名称UserName
}

func isEmpty(str string) bool {
	return len(strings.TrimSpace(str)) == 0
}
func (cp *columnField) getSqlSegment() string {
	if isEmpty(cp.prefix) {
		return cp.column
	} else {
		var sql strings.Builder
		sql.WriteString(cp.prefix)
		sql.WriteString(".")
		sql.WriteString(cp.column)
		return ""
	}

}

/** SQL拼接关键字 **/
type sqlKeyword struct {
	keyword string
}

func (sk *sqlKeyword) getSqlSegment() string {
	return sk.keyword
}

/** SQL拼接 字段对应值Value 对象 **/
type columnValue struct {
	value any
}

func (cv *columnValue) getSqlSegment() string {
	return cv.value.(string)
}

// Eq 等于 =
func (q *QueryWrapper[T]) Eq(fieldName any, value any, prefixes ...string) *QueryWrapper[T] {
	var prefix = getPrefix(prefixes...)
	var columnName = getColumnName(fieldName)
	fmt.Println(columnName)
	var sql = q.BuildWhereSQL(prefix, fieldName, internal.Eq, value)
	q.addWhereSqlQuery(sql...)
	return q
}

// Ne 不等于 !=
func (q *QueryWrapper[T]) Ne(fieldName any, value any, prefixes ...string) *QueryWrapper[T] {
	var prefix = getPrefix(prefixes...)
	var sql = q.BuildWhereSQL(prefix, fieldName, internal.Ne, value)
	q.addWhereSqlQuery(sql...)
	return q
}

// Gt 大于 >
func (q *QueryWrapper[T]) Gt(fieldName any, value any, prefixes ...string) *QueryWrapper[T] {
	var prefix = getPrefix(prefixes...)
	var sql = q.BuildWhereSQL(prefix, fieldName, internal.Gt, value)
	q.addWhereSqlQuery(sql...)
	return q
}

// Ge 大于等于 >=
func (q *QueryWrapper[T]) Ge(fieldName any, value any, prefixes ...string) *QueryWrapper[T] {
	var prefix = getPrefix(prefixes...)
	var sql = q.BuildWhereSQL(prefix, fieldName, internal.Ge, value)
	q.addWhereSqlQuery(sql...)
	return q
}

// Lt 小于 <
func (q *QueryWrapper[T]) Lt(fieldName any, value any, prefixes ...string) *QueryWrapper[T] {
	var prefix = getPrefix(prefixes...)
	var sql = q.BuildWhereSQL(prefix, fieldName, internal.Lt, value)
	q.addWhereSqlQuery(sql...)
	return q
}

// Le 小于等于 <=
func (q *QueryWrapper[T]) Le(fieldName any, value any, prefixes ...string) *QueryWrapper[T] {
	var prefix = getPrefix(prefixes...)
	var sql = q.BuildWhereSQL(prefix, fieldName, internal.Le, value)
	q.addWhereSqlQuery(sql...)
	return q
}

// Like 模糊 LIKE '%值%'
func (q *QueryWrapper[T]) Like(fieldName any, value any, prefixes ...string) *QueryWrapper[T] {
	s := fmt.Sprintf("%v", value)
	var prefix = getPrefix(prefixes...)
	var sql = q.BuildWhereSQL(prefix, fieldName, internal.Like, "%"+s+"%")
	q.addWhereSqlQuery(sql...)
	return q
}

// NotLike 非模糊 NOT LIKE '%值%'
func (q *QueryWrapper[T]) NotLike(fieldName any, value any, prefixes ...string) *QueryWrapper[T] {
	s := fmt.Sprintf("%v", value)
	var prefix = getPrefix(prefixes...)
	var sql = q.BuildWhereSQL(prefix, fieldName, internal.Not+" "+internal.Like, "%"+s+"%")
	q.addWhereSqlQuery(sql...)
	return q
}

// LikeLeft 左模糊 LIKE '%值'
func (q *QueryWrapper[T]) LikeLeft(fieldName any, value any, prefixes ...string) *QueryWrapper[T] {
	s := fmt.Sprintf("%v", value)
	var prefix = getPrefix(prefixes...)
	var sql = q.BuildWhereSQL(prefix, fieldName, internal.Like, "%"+s)
	q.addWhereSqlQuery(sql...)
	return q
}

// NotLikeLeft 非左模糊 NOT LIKE '%值'
func (q *QueryWrapper[T]) NotLikeLeft(fieldName any, value any, prefixes ...string) *QueryWrapper[T] {
	s := fmt.Sprintf("%v", value)
	var prefix = getPrefix(prefixes...)
	var sql = q.BuildWhereSQL(prefix, fieldName, internal.Not+" "+internal.Like, "%"+s)
	q.addWhereSqlQuery(sql...)
	return q
}

// LikeRight 右模糊 LIKE '值%'
func (q *QueryWrapper[T]) LikeRight(fieldName any, value any, prefixes ...string) *QueryWrapper[T] {
	s := fmt.Sprintf("%v", value)
	var prefix = getPrefix(prefixes...)
	var sql = q.BuildWhereSQL(prefix, fieldName, internal.Like, s+"%")
	q.addWhereSqlQuery(sql...)
	return q
}

// NotLikeRight 非右模糊 NOT LIKE '值%'
func (q *QueryWrapper[T]) NotLikeRight(fieldName any, value any, prefixes ...string) *QueryWrapper[T] {
	s := fmt.Sprintf("%v", value)
	var prefix = getPrefix(prefixes...)
	var sql = q.BuildWhereSQL(prefix, fieldName, internal.Not+" "+internal.Like, s+"%")
	q.addWhereSqlQuery(sql...)
	return q
}

// IsNull 是否为空 字段 IS NULL
func (q *QueryWrapper[T]) IsNull(fieldName any, prefixes ...string) *QueryWrapper[T] {
	var prefix = getPrefix(prefixes...)
	var sql = q.BuildWhereSQL(prefix, fieldName, internal.IsNull, "")
	q.addWhereSqlQuery(sql...)
	return q
}

// IsNotNull 是否非空 字段 IS NOT NULL
func (q *QueryWrapper[T]) IsNotNull(fieldName any, prefixes ...string) *QueryWrapper[T] {
	var prefix = getPrefix(prefixes...)
	var sql = q.BuildWhereSQL(prefix, fieldName, internal.IsNotNull, "")
	q.addWhereSqlQuery(sql...)
	return q
}

// In 字段 IN (值1, 值2, ...)
func (q *QueryWrapper[T]) In(fieldName any, value any, prefixes ...string) *QueryWrapper[T] {
	var prefix = getPrefix(prefixes...)
	var sql = q.BuildWhereSQL(prefix, fieldName, internal.In, value)
	q.addWhereSqlQuery(sql...)
	return q
}

// NotIn 字段 NOT IN (值1, 值2, ...)
func (q *QueryWrapper[T]) NotIn(fieldName any, value any, prefixes ...string) *QueryWrapper[T] {
	var prefix = getPrefix(prefixes...)
	var sql = q.BuildWhereSQL(prefix, fieldName, internal.Not+" "+internal.In, value)
	q.addWhereSqlQuery(sql...)
	return q
}

/** Between  值1 AND 值2 **/
func (q *QueryWrapper[T]) Between(fieldName any, start, end any, prefixes ...string) *QueryWrapper[T] {
	var prefix = getPrefix(prefixes...)
	var sql = q.BuildWhereSQLs(prefix, fieldName, internal.Between, start, internal.And, end)
	q.addWhereSqlQuery(sql...)
	return q
}

// NotBetween NOT BETWEEN 值1 AND 值2
func (q *QueryWrapper[T]) NotBetween(fieldName any, start, end any, prefixes ...string) *QueryWrapper[T] {
	var prefix = getPrefix(prefixes...)
	var sql = q.BuildWhereSQLs(prefix, fieldName, internal.Not+" "+internal.Between, start, internal.And, end)
	q.addWhereSqlQuery(sql...)
	return q
}

// HAVING SQl语句
func (q *QueryWrapper[T]) Having(having string, args ...any) *QueryWrapper[T] {
	q.havingBuilder.WriteString(having)
	if len(args) == 1 {
		// 兼容function方法中in返回切片类型数据
		if anes, ok := args[0].([]any); ok {
			q.havingArgs = append(q.havingArgs, anes...)
			return q
		}
	}
	q.havingArgs = append(q.havingArgs, args...)
	return q
}

// And 拼接 AND
func (q *QueryWrapper[T]) And(fn ...func(q *QueryWrapper[T])) *QueryWrapper[T] {
	q.addWhereSqlQuery(&sqlKeyword{keyword: internal.And})
	if len(fn) > 0 {
		nestQuery := &QueryWrapper[T]{}
		fn[0](nestQuery)
		q.whereSqlQuery = append(q.whereSqlQuery, nestQuery)
		q.last = nestQuery
		return q
	}
	return q
}

// Or 拼接 OR
func (q *QueryWrapper[T]) Or(fn ...func(q *QueryWrapper[T])) *QueryWrapper[T] {
	q.addWhereSqlQuery(&sqlKeyword{keyword: internal.Or})
	if len(fn) > 0 {
		nestQuery := &QueryWrapper[T]{}
		fn[0](nestQuery)
		q.whereSqlQuery = append(q.whereSqlQuery, nestQuery)
		q.last = nestQuery
		return q
	}
	return q
}

// Distinct 去除重复字段值
func (q *QueryWrapper[T]) Distinct(fieldNames ...any) *QueryWrapper[T] {
	for _, fieldName := range fieldNames {
		q.selects.distinctColumns = append(q.selects.distinctColumns, q.GetColumnName(fieldName))
	}
	return q
}

// Group 分组：GROUP BY 字段1,字段2
func (q *QueryWrapper[T]) Group(fieldNames ...any) *QueryWrapper[T] {
	for _, fieldName := range fieldNames {
		columnName := q.GetColumnName(fieldName)
		if q.groupBuilder.Len() > 0 {
			q.groupBuilder.WriteString(internal.Comma)
		}
		q.groupBuilder.WriteString(columnName)
	}
	return q
}

// OrderByDesc 排序：ORDER BY 字段1,字段2 Desc
func (q *QueryWrapper[T]) OrderByDesc(fieldNames ...any) *QueryWrapper[T] {
	var columnNames []string
	for _, fieldName := range fieldNames {
		columnName := q.GetColumnName(fieldName)
		columnNames = append(columnNames, columnName)
	}
	q.BuildOrder(internal.Desc, columnNames...)
	return q
}

// OrderByAsc 排序：ORDER BY 字段1,字段2 ASC
func (q *QueryWrapper[T]) OrderByAsc(fieldNames ...any) *QueryWrapper[T] {
	var columnNames []string
	for _, fieldName := range fieldNames {
		columnName := q.GetColumnName(fieldName)
		columnNames = append(columnNames, columnName)
	}
	q.BuildOrder(internal.Asc, columnNames...)
	return q
}

// Select 查询字段
func (q *QueryWrapper[T]) Select(fieldNames ...any) *QueryWrapper[T] {
	for _, fieldName := range fieldNames {
		columnName := q.GetColumnName(fieldName)
		q.selects.selectColumns = append(q.selects.selectColumns, columnName)
	}
	return q
}

// Omit 忽略字段
func (q *QueryWrapper[T]) Omit(columns ...any) *QueryWrapper[T] {
	for _, v := range columns {
		columnName := q.GetColumnName(v)
		q.selects.omitColumns = append(q.selects.omitColumns, columnName)
	}
	return q
}

// Set 设置更新的字段
func (q *QueryWrapper[T]) Set(fieldName any, value any) *QueryWrapper[T] {
	columnName := q.GetColumnName(fieldName)
	if q.updates.updateMap == nil {
		q.updates.updateMap = make(map[string]any)
	}
	q.updates.updateMap[columnName] = value
	return q
}

func (q *QueryWrapper[T]) IsNotKeyword(isKeyword bool, expressions []any) bool {
	return !isKeyword && len(expressions) > 0
}

func (q *QueryWrapper[T]) IsLastNotAndOr(lastKeyword *sqlKeyword, isKeyword bool, expressions []any) bool {
	return isKeyword && lastKeyword.keyword != internal.And && lastKeyword.keyword != internal.Or && len(expressions) > 0
}

func (q *QueryWrapper[T]) BuildWhereSQL(prefix string, fieldName any, keyword string, value any) []SqlSegment {
	var sqlSegments []SqlSegment
	var sqlKey = &sqlKeyword{keyword: keyword}
	var sqlValue = &columnValue{value: value}
	//var fieldStr = utils.GetFieldName(fieldName)
	var columnName = getColumnName(fieldName)
	var column = goutils.ToSnakeCase(columnName)
	var sqlField = &columnField{prefix: prefix, fieldName: fieldName, column: column}
	sqlSegments = append(sqlSegments, sqlField, sqlKey, sqlValue)

	return sqlSegments
}

// var sqlKey = &sqlKeyword{keyword: keyword}
// var sqlKey2 = &sqlKeyword{keyword: keyword2}
// var sqlValue = &columnValue{value: value}
// var sqlValue2 = &columnValue{value: value2}
// sqlSegments = append(sqlSegments, sqlField, sqlKey, sqlValue, sqlKey2, sqlValue2)
func (q *QueryWrapper[T]) BuildWhereSQLs(prefix string, fieldName any, keywordValues ...any) []SqlSegment {
	var sqlSegments []SqlSegment
	var columnName = goutils.ToSnakeCase(fieldName.(string))
	var sqlField = &columnField{prefix: prefix, fieldName: fieldName, column: columnName}
	sqlSegments = append(sqlSegments, sqlField)
	for i, val := range keywordValues {
		if i%2 == 0 {
			var sqlKey = sqlKeyword{keyword: val.(string)}
			sqlSegments = append(sqlSegments, &sqlKey)
		} else {
			var value = columnValue{value: val}
			sqlSegments = append(sqlSegments, &value)
		}

	}
	return sqlSegments
}

func (q *QueryWrapper[T]) BuildOrder(orderType string, columns ...string) {
	for _, v := range columns {
		if q.orderBuilder.Len() > 0 {
			q.orderBuilder.WriteString(internal.Comma)
		}
		q.orderBuilder.WriteString(v)
		q.orderBuilder.WriteString(" ")
		q.orderBuilder.WriteString(orderType)
	}
}

func (q *QueryWrapper[T]) GetColumnName(fieldName any) string {
	var columnName = goutils.ToSnakeCase(fieldName.(string))
	return columnName
}

func (q *QueryWrapper[T]) GetWhere() string {
	return q.whereFormat
}

func (q *QueryWrapper[T]) GetWhereValue() []any {
	return q.whereArgs
}

func (q *QueryWrapper[T]) addWhereSqlQuery(sqlSegments ...SqlSegment) {
	if len(sqlSegments) == 1 {
		// 如何是第一次设置，则不需要添加and(),or(),防止用户首次设置条件错误
		q.addWhereKeyword(sqlSegments[0])
		return
	}
	// 如果符合条件，则添加and。
	q.addWhereAndCondIfNeed()

	for _, sqlSegment := range sqlSegments {
		q.whereSqlQuery = append(q.whereSqlQuery, sqlSegment)
	}

	if len(sqlSegments) > 0 {
		q.last = sqlSegments[len(sqlSegments)-1]
	}
}

func (q *QueryWrapper[T]) addWhereAndCondIfNeed() {
	// 1.如果上一个不是keyword，并且表达式切片不为空，则添加and。
	// 2.如果上一个值不是and或or,并且表达式切片不为空,则添加and。
	lastKeyword, isKeyword := q.last.(*sqlKeyword)
	if q.IsNotKeyword(isKeyword, q.whereSqlQuery) || q.IsLastNotAndOr(lastKeyword, isKeyword, q.whereSqlQuery) {
		sk := sqlKeyword{keyword: internal.And}
		q.whereSqlQuery = append(q.whereSqlQuery, &sk)
		q.last = &sk
	}
}

func (q *QueryWrapper[T]) addWhereKeyword(sqlSegment SqlSegment) {
	// 如何是第一次设置，则不需要添加and(),or(),防止用户首次设置条件错误
	if len(q.whereSqlQuery) == 0 {
		return
	}

	// 防止用户重复设置and(),or()
	isKeywordRepeat := q.addWhereKeywordRepeat(sqlSegment)

	// 如果不是重复设置，则添加
	if !isKeywordRepeat {
		q.whereSqlQuery = append(q.whereSqlQuery, sqlSegment)
		q.last = sqlSegment
	}
}

func (q *QueryWrapper[T]) addWhereKeywordRepeat(sqlSegment SqlSegment) bool {
	currentKeyword, isCurrentKeyword := sqlSegment.(*sqlKeyword)
	lastKeyword, isLastKeyword := q.last.(*sqlKeyword)
	if isCurrentKeyword && isLastKeyword {
		// 如果上一次是and，这一次也是and，那么就不需要重复设置了
		isAnd := lastKeyword.keyword == internal.And
		if isAnd && currentKeyword.keyword == internal.And {
			return true
		}
		// 如果上一次是or，这一次也是or，那么就不需要重复设置了
		isOr := lastKeyword.keyword == internal.Or
		if isOr && currentKeyword.keyword == internal.Or {
			return true
		}
		// 如果上一次是and，这次是or，那么删除上一次的值，使用最新的值
		// 如果上一次是or，这次是and，那么删除上一次的值，使用最新的值
		q.whereSqlQuery = append(q.whereSqlQuery, q.whereSqlQuery[:len(q.whereSqlQuery)-1]...)
	}
	return false
}

/** 判断入参是否为空值 **/
func getPrefix(prefixes ...string) string {
	var prefix = ""
	if len(prefixes) != 0 {
		prefix = prefixes[1]
	}
	return prefix
}
func (q *QueryWrapper[T]) ExplainQueryWrapper() {
	var whereList = make([]any, 0)
	var sqlBuilder strings.Builder
	var whereFormat, whereArgs = explainSqlWhereArgs[T](q.whereSqlQuery, &sqlBuilder, whereList)
	q.whereFormat = whereFormat
	q.whereArgs = whereArgs
}

func explainSqlWhereArgs[T any](whereSqlQuery []any, sqlWhereFormat *strings.Builder, whereArgs []any) (string, []any) {
	for _, sqlSegment := range whereSqlQuery {
		switch segment := sqlSegment.(type) {
		case *columnField:
			sqlWhereFormat.WriteString(segment.getSqlSegment() + " ")
		case *sqlKeyword:
			sqlWhereFormat.WriteString(segment.getSqlSegment() + " ")
		case *columnValue:
			if segment.value == internal.And {
				sqlWhereFormat.WriteString(segment.value.(string) + " ")
				continue
			}
			if segment.value != "" {
				sqlWhereFormat.WriteString("? ")
				whereArgs = append(whereArgs, segment.value)
			}
		case *QueryWrapper[T]:
			sqlWhereFormat.WriteString(internal.LeftBracket + " ")
			// 递归处理条件
			var sql, array = explainSqlWhereArgs[T](segment.whereSqlQuery, sqlWhereFormat, whereArgs)
			sqlWhereFormat.WriteString(sql)
			whereArgs = append(whereArgs, array)
			sqlWhereFormat.WriteString(internal.RightBracket + " ")
		}

	}
	var sqlWhere = sqlWhereFormat.String()
	return sqlWhere, whereArgs
}
